﻿using System;
using System.Collections.Generic;
using System.IO;
using System.CodeDom.Compiler;
using System.Reflection;
using System.Reflection.Emit;
using System.Threading;

namespace ForgeCraftServer.Plugins
{
    public class Plugin_Manager
    {
        internal Plugin_Manager()
        {

        }

        #region Private Vars
        private List<Plugin> loaded = new List<Plugin>();
        #endregion


        #region Plublic Methods
        /// <summary>
        /// Unload a plugin from the server
        /// </summary>
        /// <param name="p">The plugin to unload</param>
        /// <param name="shutdown">Is the server shutting down</param>
        public void Unload(Plugin p, bool shutdown)
        {
            p.Unload(shutdown);
            loaded.Remove(p);
            ForgeCraft.Log(p.name + " unloaded!");
        }

        /// <summary>
        /// Unload a plugin from the server
        /// </summary>
        /// <param name="name">The name of the plugin</param>
        /// <param name="shutdown">Is the server shutting down</param>
        public void Unload(string name, bool shutdown)
        {
            if (Find(name) == null)
                return;
            Plugin p = Find(name);
            Unload(p, shutdown);
        }

        /// <summary>
        /// Unload a plugin from the server
        /// </summary>
        /// <param name="p">The plugin to unload</param>
        public void Unload(Plugin p)
        {
            Unload(p, false);
        }

        /// <summary>
        /// Unload a plugin from the server
        /// </summary>
        /// <param name="name">The plugin name</param>
        public void Unload(string name)
        {
            Unload(name, false);
        }

        /// <summary>
        /// Look for a plugin loaded on the server
        /// </summary>
        /// <param name="name">The name of the plugin</param>
        /// <returns>The plugin</returns>
        public Plugin Find(string name)
        {
            Plugin p = null;
            foreach (Plugin l in loaded)
            {
                if (l.name.ToLower() == name.ToLower())
                    return l;
                else if (l.name.ToLower().IndexOf(name.ToLower()) != -1)
                {
                    if (p == null)
                        p = l;
                    else //If we find more than 1 with a similar name, then we return null
                        return null;
                }
            }
            return p;
        }
        
        /// <summary>
        /// Check to see if a plugin is loaded on the server
        /// </summary>
        /// <param name="name">The name of the plugin</param>
        /// <returns>true if the plugin is loaded, false if it isnt</returns>
        public bool IsLoaded(string name)
        {
            return Find(name) != null;
        }

        /// <summary>
        /// Load a plugin into the server
        /// </summary>
        /// <param name="filepath">The filepath of the plugin</param>
        /// <param name="startup">Wether or not the server is starting up</param>
        public void Load(string filepath, bool startup)
        {
            String creator = "";
            try
            {
                object instance = null;
                Assembly lib = null;
                using (FileStream fs = File.Open(filepath, FileMode.Open))
                {
                    using (MemoryStream ms = new MemoryStream())
                    {
                        byte[] buffer = new byte[1024];
                        int read = 0;
                        while ((read = fs.Read(buffer, 0, 1024)) > 0)
                            ms.Write(buffer, 0, read);
                        lib = Assembly.Load(ms.ToArray());
                        ms.Close();
                        ms.Dispose();
                    }
                    fs.Close();
                    fs.Dispose();
                }
                try
                {
                    foreach (Type t in lib.GetTypes())
                    {
                        if (t.BaseType == typeof(Plugin))
                        {
                            instance = Activator.CreateInstance(t);
                            break;
                        }
                    }
                }
                catch { }
                if (instance == null)
                {
                    ForgeCraft.Log("The plugin " + filepath + " couldnt be loaded!");
                    return;
                }
                if (((Plugin)instance).version > ForgeCraft.Version)
                {
                    ForgeCraft.Log("This plugin (" + ((Plugin)instance).name + ") isnt compatible with this version of ForgeCraft!");
                    Thread.Sleep(1000);
                    //Add later?
                    /*if (Server.unsafe_plugin)
                    {
                        Server.s.Log("Will attempt to load!");
                        goto here;
                    }
                    else
                        return;*/
                }
                loaded.Add((Plugin)instance);
                creator = ((Plugin)instance).creator;
                ((Plugin)instance).Load(startup);
                ForgeCraft.Log("Plugin: " + ((Plugin)instance).name + " loaded...");
            }
            catch (FileNotFoundException e)
            {
                AlertError(e, creator, filepath);
            }
            catch (BadImageFormatException e)
            {
                AlertError(e, creator, filepath);
            }
            catch (PathTooLongException e)
            {
                AlertError(e, creator, filepath);
            }
            catch (FileLoadException e)
            {
                AlertError(e, creator, filepath);
            }
            catch (Exception e)
            {
                AlertError(e, creator, filepath);
            }

        }

        /// <summary>
        /// Load a plugin into the server
        /// </summary>
        /// <param name="filepath">The filepath of the plugin</param>
        public void Load(string filepath)
        {
            Load(filepath, false);
        }

        /// <summary>
        /// Load all the plugin in the plugins folder into the server
        /// </summary>
        public void LoadAll()
        {
            LoadAll(false);
        }

        /// <summary>
        /// Load all the plugins in the plugins folder into the server
        /// </summary>
        /// <param name="startup">Wether the server is starting up or not</param>
        public void LoadAll(bool startup)
        {
            if (Directory.Exists("plugins"))
            {
                foreach (string file in Directory.GetFiles("plugins", "*.dll"))
                {
                    Load(file, startup);
                }
            }
            else
                Directory.CreateDirectory("plugins");
        }

        /// <summary>
        /// Unload all the plugins that are loaded on the server
        /// </summary>
        /// <param name="shutdown">Wether the server is shutting down</param>
        public void UnloadAll(bool shutdown)
        {
            loaded.ForEach(delegate(Plugin p)
            {
                Unload(p, shutdown);
            });
        }

        /// <summary>
        /// Unload all the plugins that are loaded on the server
        /// </summary>
        public void UnloadAll()
        {
            UnloadAll(false);
        }
        #endregion


        #region Methods
        private void AlertError(object e, string creator, string filepath)
        {
            ForgeCraft.Log("" + e);
            //Server.ErrorLog(e);
            ForgeCraft.Log("The plugin " + filepath + " failed to load!");
            if (creator != "")
                ForgeCraft.Log("You can go bug " + creator + " about it");
            Thread.Sleep(1000);
        }
        #endregion
    }
}
